{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Kaggle 10 monkeys 图像分类实战"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0.0\n",
      "sys.version_info(major=3, minor=6, micro=10, releaselevel='final', serial=0)\n",
      "matplotlib 3.1.2\n",
      "numpy 1.18.1\n",
      "pandas 0.25.3\n",
      "sklearn 0.22.1\n",
      "tensorflow 2.0.0\n",
      "tensorflow_core.keras 2.2.4-tf\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "\n",
    "print(tf.__version__)\n",
    "print(sys.version_info)\n",
    "for module in mpl,np,pd,sklearn,tf,keras:\n",
    "    print(module.__name__,module.__version__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 Physical GPUs, 1 Logical GPUs\n"
     ]
    }
   ],
   "source": [
    "def solve_cudnn_error():\n",
    "    gpus = tf.config.experimental.list_physical_devices('GPU')\n",
    "    if gpus:\n",
    "        try:\n",
    "            # Currently, memory growth needs to be the same across GPUs\n",
    "            for gpu in gpus:\n",
    "                tf.config.experimental.set_memory_growth(gpu, True)\n",
    "            logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n",
    "            print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n",
    "        except RuntimeError as e:\n",
    "            # Memory growth must be set before GPUs have been initialized\n",
    "            print(e)\n",
    "\n",
    "solve_cudnn_error()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "True\n",
      "['n0', 'n1', 'n2', 'n3', 'n4', 'n5', 'n6', 'n7', 'n8', 'n9']\n",
      "['n0', 'n1', 'n2', 'n3', 'n4', 'n5', 'n6', 'n7', 'n8', 'n9']\n"
     ]
    }
   ],
   "source": [
    "# 判断文件存在\n",
    "train_dir = 'data/10_monkeys/training'\n",
    "valid_dir = 'data/10_monkeys/validation'\n",
    "label_file = 'data/10_monkeys/monkey_labels.txt'\n",
    "print(os.path.exists(train_dir))\n",
    "print(os.path.exists(valid_dir))\n",
    "print(os.path.exists(label_file))\n",
    "\n",
    "print(os.listdir(train_dir))\n",
    "print(os.listdir(valid_dir))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   Label     Latin Name              Common Name                     \\\n",
      "0  n0         alouatta_palliata\\t    mantled_howler                   \n",
      "1  n1        erythrocebus_patas\\t    patas_monkey                     \n",
      "2  n2        cacajao_calvus\\t        bald_uakari                      \n",
      "3  n3        macaca_fuscata\\t        japanese_macaque                 \n",
      "4  n4       cebuella_pygmea\\t        pygmy_marmoset                   \n",
      "5  n5       cebus_capucinus\\t        white_headed_capuchin            \n",
      "6  n6       mico_argentatus\\t        silvery_marmoset                 \n",
      "7  n7      saimiri_sciureus\\t        common_squirrel_monkey           \n",
      "8  n8       aotus_nigriceps\\t        black_headed_night_monkey        \n",
      "9  n9       trachypithecus_johnii    nilgiri_langur                   \n",
      "\n",
      "    Train Images    Validation Images  \n",
      "0             131                  26  \n",
      "1             139                  28  \n",
      "2             137                  27  \n",
      "3             152                  30  \n",
      "4             131                  26  \n",
      "5             141                  28  \n",
      "6             132                  26  \n",
      "7             142                  28  \n",
      "8             133                  27  \n",
      "9             132                  26  \n"
     ]
    }
   ],
   "source": [
    "# 读取labels\n",
    "labels = pd.read_csv(label_file, header=0)\n",
    "print(labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 对图像进行数据增强"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 1098 images belonging to 10 classes.\n",
      "Found 272 images belonging to 10 classes.\n",
      "1098 272\n"
     ]
    }
   ],
   "source": [
    "# 将图片转化成同一尺寸\n",
    "height = 128\n",
    "width = 128\n",
    "channels = 3\n",
    "batch_size = 64\n",
    "num_classes = 10\n",
    "\n",
    "# 读取图片并增强\n",
    "train_datagen = keras.preprocessing.image.ImageDataGenerator(\n",
    "    rescale= 1./255, # 缩放到0-1之间\n",
    "    rotation_range= 40, # 旋转范围\n",
    "    width_shift_range= 0.2, # 水平位移\n",
    "    height_shift_range= 0.2, # 竖直平移\n",
    "    shear_range= 0.2, # 剪切范围\n",
    "    zoom_range= 0.2, # 缩放范围\n",
    "    horizontal_flip= True, # 随机水平翻转\n",
    "    fill_mode= 'nearest', # 对空白位置的填充规则\n",
    ")\n",
    "# 按照文件夹进行分类，进行数据增强\n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "    train_dir, target_size = (height, width), batch_size = batch_size,\n",
    "    seed = 7, shuffle = True, class_mode = 'categorical')\n",
    "\n",
    "valid_datagen = keras.preprocessing.image.ImageDataGenerator(\n",
    "    rescale= 1./255, # 缩放到0-1之间\n",
    ")\n",
    "valid_generator = valid_datagen.flow_from_directory(\n",
    "    valid_dir, target_size = (height, width), batch_size = batch_size,\n",
    "    seed = 7, shuffle = False, class_mode = 'categorical')\n",
    "\n",
    "train_num = train_generator.samples\n",
    "valid_num = valid_generator.samples\n",
    "print(train_num, valid_num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 128, 128, 3) (64, 10)\n",
      "[[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
      " [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]]\n"
     ]
    }
   ],
   "source": [
    "for i in range(1):\n",
    "    x, y = train_generator.next()\n",
    "    print(x.shape, y.shape)\n",
    "    print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基础模型构建--CNN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d (Conv2D)              (None, 128, 128, 32)      896       \n",
      "_________________________________________________________________\n",
      "conv2d_1 (Conv2D)            (None, 128, 128, 32)      9248      \n",
      "_________________________________________________________________\n",
      "max_pooling2d (MaxPooling2D) (None, 64, 64, 32)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 64, 64, 64)        18496     \n",
      "_________________________________________________________________\n",
      "conv2d_3 (Conv2D)            (None, 64, 64, 64)        36928     \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 32, 32, 64)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_4 (Conv2D)            (None, 32, 32, 128)       73856     \n",
      "_________________________________________________________________\n",
      "conv2d_5 (Conv2D)            (None, 32, 32, 128)       147584    \n",
      "_________________________________________________________________\n",
      "max_pooling2d_2 (MaxPooling2 (None, 16, 16, 128)       0         \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 32768)             0         \n",
      "_________________________________________________________________\n",
      "dense (Dense)                (None, 128)               4194432   \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 10)                1290      \n",
      "=================================================================\n",
      "Total params: 4,482,730\n",
      "Trainable params: 4,482,730\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model = keras.models.Sequential([\n",
    "    keras.layers.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu',\n",
    "                        input_shape=[width, height, channels]),\n",
    "    keras.layers.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu'),\n",
    "    keras.layers.MaxPool2D(pool_size=2),\n",
    "    \n",
    "    keras.layers.Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'),\n",
    "    keras.layers.Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'),\n",
    "    keras.layers.MaxPool2D(pool_size=2),\n",
    "    \n",
    "    keras.layers.Conv2D(filters=128, kernel_size=3, padding='same', activation='relu'),\n",
    "    keras.layers.Conv2D(filters=128, kernel_size=3, padding='same', activation='relu'),\n",
    "    keras.layers.MaxPool2D(pool_size=2),\n",
    "    \n",
    "    keras.layers.Flatten(),\n",
    "    keras.layers.Dense(128, activation='relu'),\n",
    "    keras.layers.Dense(num_classes, activation='softmax'),\n",
    "])\n",
    "\n",
    "model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "17/17 [==============================] - 29s 2s/step - loss: 2.3178 - accuracy: 0.1006 - val_loss: 2.2766 - val_accuracy: 0.1211\n",
      "Epoch 2/10\n",
      "17/17 [==============================] - 21s 1s/step - loss: 2.2352 - accuracy: 0.1605 - val_loss: 1.9845 - val_accuracy: 0.3008\n",
      "Epoch 3/10\n",
      "17/17 [==============================] - 21s 1s/step - loss: 1.9911 - accuracy: 0.2814 - val_loss: 1.6760 - val_accuracy: 0.3711\n",
      "Epoch 4/10\n",
      "17/17 [==============================] - 20s 1s/step - loss: 1.7856 - accuracy: 0.3443 - val_loss: 1.6182 - val_accuracy: 0.4062\n",
      "Epoch 5/10\n",
      "17/17 [==============================] - 20s 1s/step - loss: 1.8522 - accuracy: 0.3230 - val_loss: 1.5182 - val_accuracy: 0.4648\n",
      "Epoch 6/10\n",
      "17/17 [==============================] - 19s 1s/step - loss: 1.5844 - accuracy: 0.4043 - val_loss: 1.5110 - val_accuracy: 0.4727\n",
      "Epoch 7/10\n",
      "17/17 [==============================] - 20s 1s/step - loss: 1.5280 - accuracy: 0.4304 - val_loss: 1.4657 - val_accuracy: 0.4883\n",
      "Epoch 8/10\n",
      "17/17 [==============================] - 20s 1s/step - loss: 1.5101 - accuracy: 0.4540 - val_loss: 1.4864 - val_accuracy: 0.4961\n",
      "Epoch 9/10\n",
      "17/17 [==============================] - 19s 1s/step - loss: 1.4998 - accuracy: 0.4487 - val_loss: 1.3247 - val_accuracy: 0.5312\n",
      "Epoch 10/10\n",
      "17/17 [==============================] - 19s 1s/step - loss: 1.3863 - accuracy: 0.4913 - val_loss: 1.3946 - val_accuracy: 0.4805\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "epochs = 10\n",
    "history = model.fit_generator(train_generator,\n",
    "                              steps_per_epoch= train_num // batch_size,\n",
    "                              epochs=epochs,\n",
    "                              validation_data=valid_generator,\n",
    "                              validation_steps= valid_num // batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])\n"
     ]
    }
   ],
   "source": [
    "print(history.history.keys())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeoAAAEzCAYAAAD+XEDdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8li6FKAAAgAElEQVR4nO3deXgV1f3H8fe592ZfgSRkYycEkE0W2QQCuCKK1g3rglalaOuutVVrbatdpNXa1h9qXbFuFNCioraiIYBiEWTfRBYJa0AJCZD9/P6YCwkQJJGEmSSf1/PMc+/MnXvzzbB87jkzc46x1iIiIiLe5HO7ABERETk2BbWIiIiHKahFREQ8TEEtIiLiYQpqERERD1NQi4iIeNhxg9oY08oY87ExZpUxZoUx5rZq9skyxuQbYxYHlwfrp1wREZGmJVCDfcqAu6y1i4wxMcBCY8x/rbUrj9hvjrV2dN2XKCIi0nQdt0Vtrd1mrV0UfF4ArALS6rswERERqeU5amNMW+BU4LNqXh5ojFlijHnPGHNKHdQmIiLS5NWk6xsAY0w0MA243Vq794iXFwFtrLWFxphRwFtARjWfMR4YDxAeHt6ndevW37twOb6Kigp8Pl0vWN90nOufjnH90zE+OdauXbvLWptYm/eYmoz1bYwJAd4BPrDWPlaD/TcCfa21u461T2Zmpl2zZk0tSpXays7OJisry+0yGj0d5/qnY1z/dIxPDmPMQmtt39q8pyZXfRvgOWDVsULaGJMc3A9jzGnBz91dm0JERETkaDXp+h4MXA0sM8YsDm67D2gNYK19CrgEuMkYUwYcAMZaTcslIiJywo4b1NbauYA5zj5/B/5eV0WJiIiIo8YXk4mISNNWWlpKbm4uRUVFbpfieeHh4aSnpxMSEnLCn6WgFhGRGsnNzSUmJoa2bdsSvCxJqmGtZffu3eTm5tKuXbsT/jxdiy8iIjVSVFREixYtFNLHYYyhRYsWddbzoKAWEZEaU0jXTF0eJwW1iIg0GNHR0W6XcNIpqEVERDxMQS0iIg2OtZZ77rmHbt260b17d9544w0Atm3bxtChQ+nVqxfdunVjzpw5lJeXc+211x7a9/HHH3e5+trRVd8iItLgTJ8+ncWLF7NkyRJ27dpFv379GDp0KK+++ipnn302999/P+Xl5ezfv5/FixezZcsWli9fDsCePXtcrr52FNQiIlJrv357BSu3Hjk/04npmhrLr86v2eSLc+fO5YorrsDv99OyZUuGDRvGggUL6NevHz/60Y8oLS3lwgsvpFevXrRv357169dzyy23cN5553HWWWfVad31TV3fIiLS4BxrlOqhQ4eSk5NDWloaV199NZMnT6ZZs2YsWbKErKwsnnzySW644YaTXO2JUYtaRERqraYt3/oydOhQnn76acaNG8c333xDTk4OEydOZNOmTaSlpXHjjTeyb98+Fi1axKhRowgNDeXiiy+mQ4cOXHvtta7WXlsKahERaXAuuugiPv30U3r27IkxhkcffZTk5GReeuklJk6cSEhICNHR0UyePJktW7Zw3XXXUVFRAcDvf/97l6uvHQW1iIg0GIWFhYAzoMjEiROZOHHiYa+PGzeOcePGHfW+RYsWnZT66oNr56jLNQmmiIjIcbnWot5cUMGA382iZ6s4eraKp2d6PN3T44gNP/GZRkRERBoL14K6ebhhQPvmLMnN54MVOw5t75AYdSi4e7aKp0tKDGEBv1tlioiIuMq1oI4NNfxl7KkA7NlfwtLcfJbm7mHx5nxy1u5i+qItAIT4DV1TYunZKp4e6fH0ahVH+4RofD4NDC8iIo2fJy4mi48MZWinRIZ2SgSc++O25RcdCu4lm/cwfdEWJn+6CYCYsADd0+MOBXfPVvEkx4ZrVhcREWl0XAvq0NJ8sBaqCVdjDKnxEaTGR3BOtxQAyiss6/MKWZLrBPeS3D08N3c9pcGr0pJiwg4L7h5p8cRF6ny3iIg0bK4FdVhRHrxyKVz4fxCddNz9/T5DRssYMlrGcEmfdACKSstZvb3ACe7Ne1icu4cPV1We726XEEXP9ODFaq3i6ZoSS3iIzneLiEjD4VpQF4UnwsY58H8DnbDudHatPyM8xE+vVvH0ahV/aFv+gVKWb8lncTC8P12/m7cWbwUg4DN0Tok5dKFar1bxdEiMxq/z3SIijVJ0dPShe6+PtHHjRkaPHn1osg6vci2oS0PiYPxbMO0GePUy6HcjnPVbCIk4oc+NiwhhcMcEBndMOLRte34RS3L3HOoyn7FkK6989jUAUaF+uqXF0SvY6u6RHkdafITOd4uIiCe4ezFZUhe48SOY9Rv49O9OC/viZyG5e53+mOS4cJLjkjn7lGQAKiosG3bvO9RlviQ3nxfmbaSk3BleLiE69FCru0d6HD3T42kWFVqnNYmISO3de++9tGnThptvvhmAhx56CGMMOTk5fPvtt5SWlvLwww8zZsyYWn1uUVERN910E59//jmBQIDHHnuM4cOHs2LFCq677jpKSkqoqKhg2rRppKamctlll5Gbm0t5eTm//OUvufzyy+vj1wXcDmqAQBic/Qh0GAFv3QT/GAEjfwUDbgZf/Qyc5vMZOiRG0yExmh/0ds53l5RVsHr73kPBvWTzHj5as5ODE7S0aRHJhb3SmDCsAxGhOs8tIk3cez+H7cvq9jOTu8O5f/jOXcaOHcvtt99+KKinTJnC+++/zx133EFsbCy7du1iwIABXHDBBbXqGX3yyScBWLZsGatXr+ass85i7dq1PPXUU9x2221ceeWVlJSUUF5ezsyZM0lNTeXdd98FID8//3v+wjXjflAf1HEk3PQpzLgF/nM/rPsQLpwEsSkn5ceHBnz0SHfu1b46uK2gqJRlW/JZmpvP/PW7eWLWl0xdmMsvR3fl7FNaqntcROQkO/XUU9m5cydbt24lLy+PZs2akZKSwh133EFOTg4+n48tW7awY8cOkpOTa/y5c+fO5ZZbbgGgc+fOtGnThrVr1zJw4EAeeeQRcnNz+cEPfkBGRgbdu3fn7rvv5t5772X06NEMGTKkvn5dwEtBDRDVAsa+AgtfhPd/AZMGwQV/gy6jXSknJjyEQR0SGNQhgQnDOjB//W4emrGCCf9cyJCMBH51/il0TIp2pTYREVcdp+Vbny655BKmTp3K9u3bGTt2LK+88gp5eXksXLiQkJAQ2rZtS1FRUa0+81jzW//whz+kf//+vPvuu5x99tk8++yzjBgxgoULFzJz5kx+8YtfcNZZZ/Hggw/Wxa9WLdcm5TgmY6DvdfDjHIhvBW9cCW/fBiX73K6MAe1b8M4tp/PQ+V1ZsnkP5/wlh9/NXEVBUanbpYmINBljx47l9ddfZ+rUqVxyySXk5+eTlJRESEgIH3/8MZs2bar1Zw4dOpRXXnkFgLVr1/L111+TmZnJ+vXrad++PbfeeisXXHABS5cuZevWrURGRnLVVVdx99131/vMXN5qUVeV2Amu/xA+fgTmPQEb5zoXmqWe6mpZAb+Pawe34/yeqUz8YA3/mLOeN7/Ywn2jOnNhrzR1h4uI1LNTTjmFgoIC0tLSSElJ4corr+T888+nb9++9OrVi86dO9f6M2+++WYmTJhA9+7dCQQCvPjii4SFhfHGG2/wz3/+k5CQEJKTk3nwwQdZsGAB99xzDz6fj5CQECZNmlQPv2Ulc6zmfn3LzMy0a9asqdnOG3Jg+o9h304Y8QAMuhV83riga8nmPTw4YwVLNu+hb5tm/HrMKZySGud2WQBkZ2eTlZXldhmNno5z/dMxrn81OcarVq2iS5cuJ6egRqC642WMWWit7Vubz/Fe13d12g2Fm+ZB5/Pgw4dg8hjIz3W7KgB6tornzZsG8ejFPdiwax/n/20uD7y1jD37S9wuTUREGoGGEdQAkc3h0pdgzJOwZRFMGgwr3nK7KsC53euyfq346O4srhnYltf+t5nhf8rmlc82UV7hTo+FiIg4li1bRq9evQ5b+vfv73ZZNebdc9TVMQZOvQpaD4TpN8K/xsGXVzlXH4bFuF0dcREhPHTBKYw9rRW/+vcK7n9zOa/972t+fUE3+rRp5nZ5IiJNUvfu3Vm8eLHbZXxvDadFXVWLDvCjD2DoPbDkVXhqCOR+7nZVh3ROjuX18QP42xWnsqughIsnfcJdU5aws6B2twuIiHiNW9c1NTR1eZwaZlAD+EOcC8uufRcqyuC5s2D2RKgod7sywJmq8/yeqcy6axg3Z3VgxpItjPjTbJ6ds57S4FClIiINSXh4OLt371ZYH4e1lt27dxMeHl4nn9ewur6r02YQTJgL794FHz8MX82CHzwD8a3drgyAqLAAPzunM5f2bcWv317Bw++u4o0Fm3noglMOmzhERMTr0tPTyc3NJS8vz+1SPC88PJz09PQ6+ayGH9QAEfFwyXPOVJnv3OlcaDb6ceh+iduVHdIuIYoXru3HrFU7+c07K7ny2c8Y1T2Z+8/rSlr8ic0YJiJyMoSEhNCuXTu3y2hyGm7Xd3V6XAY3zXVm5Zp2PUwfD0X1O1h6bRhjOKNrS/5zx1DuOrMTH63eycg/Z/O3WV9SVOqNLnsREfGWxhXUAM3awrUzIes+WDYVnjodvp7vdlWHCQ/xc8vIDGbdlcWIzkn8+b9rOevxHD5cuUPnfkRE5DCNL6gB/AHIuhd+9D5g4IVz4ePfQXmZ25UdJi0+gv+7sg+v3NCf0ICPGyZ/znUvLmDDLvfHNRcREW9onEF9UKvTnAvNeoyF2X+EF86Bbza4XdVRBndM4L3bhvDAeV34fOO3nP14Dn98fzX7ir31xUJERE6+xh3UAOGxcNEkuOR5yFvrdIUvfg081sUc4vdxw5D2fHT3MM7vmcqk7K8Y+efZzFiyVd3hIiJNWOMP6oO6XeyMF57SE96aAFN/BAe+dbuqoyTFhPPny3oy7aaBJMSEcutrXzD2mfms3r7X7dJERMQFTSeowZnfetzbMPJBWDUDJp3uTJ/pQX3aNOffPzmdRy7qxpodBZz317k8NGMF+Qc097WISFPStIIanOkxh9wF1/8HAmHw4miY9Rso914A+n2GK/u34eO7srjitFZM/nQjI/6UzZQFm6nQZB8iIk1C0wvqg9L6wI9zoPfVMOfP8NyZsPsrt6uqVrOoUB6+sDszfno67RKi+Nm0pVw06RMWb97jdmkiIlLPmm5QA4RFwwV/g8tehm83OheaLZrsuQvNDuqWFse/Jgzk8ct7snXPAS58ch73Tl3K7sJit0sTEZF6ctygNsa0MsZ8bIxZZYxZYYy5rZp9jDHmr8aYdcaYpcaY3vVTbj3pegHc9Amk94MZt8CUq2H/N25XVS1jDBedms5Hdw1j/ND2TFuUy/A/ZfPivA2UabIPEZFGpyYt6jLgLmttF2AA8BNjTNcj9jkXyAgu44FJdVrlyRCbCle/BWc9DGveh0mDYH2221UdU0x4CPeN6sL7tw+hZ6t4Hnp7JaP/Npf563e7XZqIiNSh4wa1tXabtXZR8HkBsApIO2K3McBk65gPxBtjUuq82vrm88GgW+DGWRAWA5MvhP/8Esq827XcMSmGyT86jaeu6kNBURljn5nPLa99wbb8A26XJiIidcDUZjANY0xbIAfoZq3dW2X7O8AfrLVzg+uzgHuttZ8f8f7xOC1uEhMT+0yZMuVE6683vvJiOnz1Amlb36Mguj2rutzJ/qhWbpf1nYrLLTPXl/LuhlL8Bs5Ot5zXKYowv3G7tEatsLCQ6Ohot8to1HSM65+O8ckxfPjwhdbavrV5T42D2hgTDcwGHrHWTj/itXeB3x8R1D+z1i481udlZmbaNWvW1KZWd6x5D/79EyjZD+f8HvpcC8bbwbf5m/389p2V/GflDppFhnDtoHZcM7ANzaJC3S6tUcrOziYrK8vtMho1HeP6p2N8chhjah3UNbrq2xgTAkwDXjkypINygarNzXRga20K8azMc+GmT6HNQHjndph2AxQXuF3Vd2rVPJJnrunL/f3D6dOmGY9/uJZBf/iIX7+9gi171CUuItKQ1OSqbwM8B6yy1j52jN1mANcEr/4eAORba7fVYZ3uimkJV06DEb+EFdPhmSzYvtztqo4ro5mfZ8f144Pbh3Ju92Re/nQTwx79mDunLGbtDm9/2RAREUdNWtSDgauBEcaYxcFllDFmgjFmQnCfmcB6YB3wD+Dm+inXRT4fDL0bxr0DxYXw7EhY+KJn77muKjM5hscu68Xsnw3n6oFteG/Zds56PIcbXlrA5xu9eRuaiIg4AsfbIXje+TtPylrnRPdP6qooT2s72Jk6883x8PZtsHEejH7cGTzF49LiI/jV+adw64gMXvp0Iy99spFLnvqUfm2bMWFYB4ZnJuHzefv8u4hIU9O0Ryb7vqITg13hD8DyqQ2mK/ygZlGh3H5GJ+b9fAQPnd+VrXuKuP6lzznniRymL8qlVAOniIh4hoL6+/L5YOg9cM0MKN4b7Ap/qUF0hR8UGRrg2sHtyL4ni8cv74nBcOeUJWRNzOaFeRvYX1LmdokiIk2egvpEtRvidIW3HgBv3wpv/tg5h92AhPh9XHRqOu/fPoTnr+1LWnwEv357JYP/8BGP/3ct3+wrcbtEEZEmS0FdF6KT4KrpMPwBWPYv+Mdw2LHS7apqzRjDiM4tmTJhINNuGkifNs15YtaXDP7DRzw0YwW53+53u0QRkSZHQV1XfH4Ydg9c828oyod/jIBFLzeorvCq+rRpzrPj+vLfO4YyqnsK/5y/iWETs7nzjcWs2a5bu0REThYFdV1rN9TpCm91Gsz4Kbw5ocF1hVeV0TKGP1/Wk5yfDefaQW15f8V2zv5LDte/uIAFurVLRKTeKajrQ3QSXP0mZN0HS99osF3hVaXGR/DL0V355OcjuPPMTnyxeQ+XPvUpF0/6hA9X7qCiomH2HIiIeJ2Cur74/JB1r9MVfmCP0xX+xT/druqExUeGcuvIDObdO4JfX3AKO/YWccPkzzn7LzlMXahbu0RE6pqCur61HxbsCu/nTO7x5k1Qss/tqk5YRKifcYPakn13Fk+M7YXfZ7j7X0sY9ujHPDd3A/uKdWuXiEhdUFCfDDEt4eq3IOsXsOQ1eGY47FzldlV1IuD3MaZXGu/dNoQXrutHq+aR/PadlQz+40c8plu7REROmIL6ZPH5IevncM1bcOBbJ6y/eMXtquqMMYbhmUm88eOBTL95EKe1bc5fZ33JoD/M0q1dIiIn4LhjfUsda5/ldIVPux7+fTNsmgejJkJolNuV1ZnerZvxzDV9WbezgKdnr+eVzzbx8vxNnN8jhQlZHeicHOt2ibVWUlbB7n3F7CooYVdhMXmFxewqLGbP/lKaF5WT5XaBItJoKajdENPSuchs9h9h9qOwZSFc+hIkdXa7sjrVMSmGiZf25M6zOvHcnA289r+veWvxVoZnJnJTVkf6tW2GM4uqO4rLytld6ATvrsJi8gqK2VVYEnw8uJQcCuTqBHyGsgrLl2VLuP+8LjSPCj3Jv4WINHYKarf4/DD8Pmg9EKbf6NzCdd5j0OsKtyurcylxETwwuiu3jMjg5fkbeWHeRi57+lN6t45nwrAOnNGlZZ3N2lVcVu6Ea0HxUYGbV1jsbA8+7i2q/oK3mLAACTFhJESHkpEUzcD2LUiIDiMhJpTE6DASYsKcx+gwjIF7XpjFjCVb+Gj1Du4b1YVL+qS7+gVERBoXBbXbOgwPdoXfAG9NgI1zg13hkW5XVufiIkP46YgMbhjSnn99vpln5qxn/MsL6ZgUzY+HtmdMrzRCA0dfNlFUWl4ldIMt4OC6E7qV3dEFxwrf8MChcO2cHENCx4RDoZsQ7YRyQnQYiTFhhIf4a/V7XdwplFvGDOT+N5dxz9SlTF2YyyMXdadjkvenPhUR71NQe0FM8tFd4Ze9BImZbldWL8JD/Fw9sC1XnNaamcu3Myn7K+6ZupTH/ruWrMwk9uwvqQzlgmIKjnGrV2x44FDQdkmNZWiVwE042PKNCaNFVGitw7e2OrWM4Y3xA/nXws38buZqRj0xhwlZHbg5q0O9/2wRadwU1F5xqCt8AEy70ZnjevTj0HOs25XVm4DfxwU9Uzm/Rwo5X+7i6dlf8d7ybbSICiUxJoxTUmMPtXKrtngTosNoER1KWMBbAejzGS7v15qRXVryyLur+OusL3l7yVYeubAbgzomuF2eiDRQCmqv6TCisiv8zR/DxjlwbuPsCj/IGMOwTokM65Todil1IiE6jMcv78XFvdN54K1l/PDZz/jBqWncf14XWkSHuV2eiDQwuo/ai2JTnK7wofc491o/OxLy1rpdldTS6RkJvH/7UG4Z0ZG3l25l5GOzmbJgM7aBzqgmIu5QUHuVPwAjHoCrpkHhTqcrfOkUt6uSWgoP8XPXWZm8d9sQOiXF8LNpS7n8mfms26mpQkWkZhTUXtdxpNMVntrLuY1rxi1QesDtqqSWOibF8Pr4ATx6cQ/W7ijg3Cfm8Of/rKGotNzt0kTE4xTUDUFsClwzA4bcDYsmwz/UFd4Q+XyGy/q1Ytadwzi/Zyp/+2gd5/wlh7lf7nK7NBHxMAV1Q+EPwMhfBrvCtwe7wv/ldlXyPbSIDuOxy3rx6g39McZw1XOfcfvrX7CrsNjt0kTEgxTUDU3HM5yu8JSeMP0GmHGrusIbqEEdE3jvtiHcOjKDmcu2M/LPs3n9f19TUaGLzUSkkoK6IYpNhXFvw+l3wqKX4NkzYNeXblcl30N4iJ87z+zEzNuG0Dk5hp9PX8ZlT3/K2h262ExEHArqhsofgDN+BVdOg71bna7wZVPdrkq+p45J0bw+fgATL+nBV3mFjHpiDhM/WK2LzUREQd3gZQS7wpO7O1Nnvn2busIbKGMMl/Ztxay7srjw1DSe/Pgrzno8h5y1eW6XJiIuUlA3BnFpMO4dOP0OWPgiPHsm7FrndlXyPTWPCuVPl/bk1Rv7E/AZrnn+f9z62hfsLChyuzQRcYGCurHwB+CMh+DKqbB3CzwzjJbbs6Fkv8uFyfc1qEMC790+hNvPyOD95ds548+zefUzXWwm0tRorO/GJuNMpyt86o/osvpx+N3jEJMKzdtD83bBx/aV62Exblcs3yEs4Of2Mzpxfs9UHnhzOfe9uYxpi3L53UXdyUzWn51IU6Cgbozi0uDad1g+fSLdWobANxvgm/Xw5X+gcMfh+0YlHR3eB59HxLtTvxylQ2I0r97Yn+mLtvDwuys5769zuHFoe24dkUFEqLdmERORuqWgbqz8IexKHARDsw7fXlwA3250gvub9bD7KyfI12fDklcP3zei+REhXmWJbA7GnKRfRsC52OziPukM75zE72euYlL2V7yzdCu/HdONrMwkt8sTkXqioG5qwmKcK8STux/9Wsn+w0P84PL1fFj2L6DKudHwuGOHeFSiQrweNY8KZeKlPbm4Tzr3v7mMa19YwOgeKTw4uitJseFulycidUxBLZVCI6FlV2c5UlkxfLvp6BDfsghWvAW2yv2+odHVnA8PLtHJ4NM1jHVhQPsWzLxtCE/PXs/fP17H7LV53HtOZ354Wmt8Pn1REmksFNRSM4EwSOzkLEcqL4U9XwfPhX9VGeI7VsDqmVBRWuVzIqqE+BFhHpuuEK+lsICfW0dmMLpHCg+8tZwH3lrOtEW5/P4H3emcHOt2eSJSBxTUcuL8IdCig7NwxuGvlZfB3twqrfDghW2718GX/4XyKhNR+INfBloPgraDncfoxJP6qzRU7ROjeeWG/ry1eAu/fWcV5/11LjcMacdtIzOIDNU/c5GGTP+CpX75A9CsrbN0GHH4axUVULD18Avbti+FL16G/z3t7JPQCdoMdpa2g51xzqVaxhguOjWdrE5J/OG91Tw9ez3vLt3Gb8d0Y3hnXWwm0lApqMU9Ph/EpTtLu6GV28tKYNsS2DTPWZZPg4UvOK81a1sZ3G0GOeu6cO0wzaJC+eMlPbi4Tzr3vbmM615cwHndU3jw/K601MVmIg2Oglq8JxAKrfo5y+m3Q0U57FgOG4PBveY9WPyKs29MqtPSbjPICe+ETgruoNPaNWfmrUN4Jucr/vrROnLW5nHPOZlc2b8Nfl1sJtJgKKjF+3x+Z/7tlJ4w8Gany3zXGie0N86DDXOCt48BkQmVod12MCSd0qQvUAsN+PjpiAxG90jlgbeW8+C/V/DqZ1/Tq1U8KXERpMSFkxwXTmp8OMlxEUSH6b8EEa/Rv0ppeHw+SOriLP1uAGudc9yb5sGmT5zHVTOcfcPjoPXAyu7ylB7OxW9NTNuEKF6+/jRmLNnK83M38OGqnewqLD5qv5iwACnB0E6JDSclPjwY5hGkBkM9JrzpHT8RNymopeEzpvKq897XONv2bK4M7U3zYO37zvaQKGjdv7LVndbHufWsCTDGMKZXGmN6pQFQXFbOzr3FbMsvYlv+AbblF7E9v4itew6wfW8Rq7btJa/g6DCPDgscaomnxIUf0TKPcMI8LIDRKQiROqGglsYpvhXEXw49L3fWC3bA158Ez3N/Ah897Gz3h0F6Pye42w52nodGuVf3SRQW8NOqeSStmkcec5+Ssgp27C1i+94iJ9D3VAb6tvwDrNleQF5hMfaICb2iQv2VwR0bDPRgiB8M99hwhblITSiopWmIaQmnXOQsAPu/ga8/rWx1z/kT5DwKvgCk9q5scbfu73SfN1GhAd9xw7y0PBjm+UVHt87zi1i7I4+dBUeHeeTBMI+LqL51HhdBbITCXERBLU1TZHPofJ6zABTthc3/q+wq//RJmPcXMD5nXPSD57hbD4SoFu7W7jEhfh/pzSJJb/bdYb6zoJjtwRDftscJ9e17D7B1TxFzv9zFzoIijpxqOyLET0pcOIHyA7y8cQGxESHEhAeCi/M8Njzk0HrswceIABEhfoW8NArHDWpjzPPAaGCntbZbNa9nAf8GNgQ3TbfW/qYuixSpd+GxkHGGs4AzQcmWzytvCfv8eZj/f85riV0qu8pbD+KopqIcJcTvIy0+grT4iGPuUxYM86pd6wdb6F/lOs/X7iygoKiMvQdKjwr1I/l9pjLUw5zwPjrcDz4//AtAbISzPSzgU9iL62rSon4R+Dsw+TgJ48gAABOVSURBVDv2mWOtHV0nFYl4QWikMwjLwYFYyoph6xeVt4QtfQM+fw6ALIA5Aafb3Bfi3E7mDznGenCp0bo/+P7jrNf2swLhEJMM0S2dkeM8IuD3kRofQWo1YZ6dnU1W1pBD69Za9peUU1BURkFRKXuPeCyo8rj3wMH1MjZ/s7/yteKy437HCvGbasP9yG2HteqDXwiiQv1EhgWIDPFrkhQ5Icf9V2qtzTHGtK3/UkQ8LBAGrQc4y5C7nDHMty+Fr+ezcfVi2rZKg4qyyqW8tAbr5VBWdPh6RXC/8oP7HmO9Lhi/E9ixqcElLbikVj7GpHgqzA8yxhAVFiAqLEBy3Pcbba2iwrKvpOxQiDtBHwz3IwJ/74HK9Y279h/6UlBYXLM/i/AQH1GhASLD/ESGBB9D/USGBogK9RMRfIwMCxAZ6q92m7M9+DzM6drXwDVNQ139CxxojFkCbAXuttauqKPPFfEmfwDSekNabzYWZ9M2K+vk/Wxrg6H+HUH+XeulB6BgG+zdCvlbYO8W2LHSmSSldP/hP8v4nJb3oTBPPzzY49KcqUsDoSfv968jPt/B1vL3vy+8vMJSWFw11IMt+OJS9hWXc6CknH0lZewvKWd/SRn7i6uul7O7cD8HSsuD+5axr6T8+D+0ioNfACIOhniVLwBVvwgcDPeqXwAiQv1EhVXut69Up3C8ytganF8LtqjfOcY56ligwlpbaIwZBTxhrc04xueMB8YDJCYm9pkyZcoJlC7HU1hYSHR0tNtlNHqN5jhbS6BsH2HFuwgr3h1c8qo830VY8S4C5UWHvw1DSWg8xWEtKA5LqOYxgeKw5ljf9w9ETxxja/GXF+EvP3BoCZQdqHabr6KEPfGn8E3zU52eixqqsJbSciguh6JyS3E5FJdZisqhOLheVBbcXm4pKqvcXlxuKS6r8r4jXq+JNrE+uif46ZHop0OcTy32ejB8+PCF1tq+tXnPCQd1NftuBPpaa3d9136ZmZl2zZo1NatSvhfnvF6W22U0ek3qOFsLxXud1vjeLcEWefB51cfivUe/Nyrx2K3y2FRn3PaQ6ruxv9cxttY5tVBcCCUFwcfCyvWSfVW2FVR5rbr14CM1bHX6Qpzei9g0OPUqZ4lvXbv661BFhaWorPyIVr7Tst9X7LT2536xks2l0Sz6eg/lFZaY8ABDMhIY1imRoZ0SSYk79oWAUnPGmFoH9Ql3fRtjkoEd1lprjDkN8AG7T/RzRcSDjHHuKw+Pc4ZwPZaivU73en5uMMAPhvgW+HYDbJoLRflHvy+yxRHnylMhLp3EnV/BF7mVgXncUA0Gs61hUzIQDqHREBYNoTHOY2SCMztbaDSExVR5vcp6aNTh7wkNLhVlsPY9WDQZZj/qLB1HQu9xkHnuSR/G1uczwe7wY/+X33zvOrKyBpF/oJR563Yxe00es9fmMXPZdgA6J8cwrFMiwzIT6dumOaGBpjuG/slWk9uzXsO5sDXBGJML/AoIAbDWPgVcAtxkjCkDDgBjbU2a6SLSeIXHOkti5rH3KS48ojW+FfYGgz1/M2yeDwe+BeAUgJVV3usLHB2gYTEQm3J4aB4ZotWuR9d9cPpCoesYZ9nzNXzxT2eZcjVEJUGvHzrD3bboULc/tw7ERYQwqnsKo7qnYK1lzY4CZq/JI3tNHs/P28DTOeuJCvUzsEMCWZmJDOuU+J0D4siJq1HXd31Q13f9a1Jdsi7Sca5HJftg71YWzJ9Lv0FZlcEcCGt405lWlMO6D2HhS87Y87Yc2g5xWtldzj9mt//JUpO/x4XFZXz61W6y1+wke00eW/YcAKB9YhRZnZIYlplI/3bNCQ+p+Xn5psaVrm8RkXoTGgUJGeyL3gLN27ldzYnx+aHT2c5SsN2ZU33RZJh+A0Q0gx5joc+47z6l4LLosABndm3JmV1bYq3lq7x9zF7rdJH/87NNPD9vA+EhPga0b8GwTolkZSbRtkWkBo05QQpqEZGTLSbZuR9/8B2wMcdpZX/+HHw2yZkYpvc46PYDT08QY4yhY1I0HZOiuf70dhwoKWf+ht2Hzm3/+u2V/PrtlbRuHhkM7UQGdmjxnefJpXo6YiIibvH5oH2Ws+zbDUtfd0J7xk/h/V9A90ucVnbqqe7WWQMRoX6GZyYxPDMJgE2795Gz1jm3PXVhLi/P30So30e/ds0OdZNnJEWrtV0DCmoRES+IagEDfwIDbobNnzmBveR1WPgCJPdwArv7pQ1mNrc2LaK4emAUVw9sS3FZOQs2fMvstTuZvTaPR2au4pGZq0iNC2dY8IK0wR0TTmjwmcZMQS0i4iXGVA5Xe87vYdm/YNFL8O5d8MEDzlStfcZBq/4N5oK6sICf0zMSOD0jgfvPgy17DpCzNo/Za/J4e8k2XvvfZgI+Q+82zQ51k3dNiVVrO0hBLSLiVRHxcNqN0O8GZ1KYRZNh2VRY8iokdnZu8ep5hTNtawOSFh/BFae15orTWlNaXsGiTd+SHQzuiR+sYeIHa0iMCXPu2+6UyJCMBOIjQ50x8Xd/BXmrYOcqZ3jbfte7/evUOwW1iIjXGXNobHnOehhWvOm0sj+4Dz58yLm9q/c453YvX8MaiCTE76N/+xb0b9+Ce8/pzM69Rcxes50VK5axZ8XbrF68CXyb6RG6lfSKLfhtcCIU44OuFyqoRUTEY8KioffVzrJjpdPKXvIaLJ8Gzdo523td6VxZ7nXWOoPb7FwNO1fCzlUk5a3i0rw1XFoWHFM+BPaEpbK2Ip13i3uytiKd7eHtSOvYg5Gd2zDK3d/gpFBQi4g0VC27wrl/gDMeglVvO63sWb+Bjx5xhirtPc4ZutTn8gAk1kLhjmAYV4YyeWuc4V4Pikl17iPvN8R5TOwCiZnEh0VzGtChsJi563aRvSaP7LV57K/YyqjuKa79WieLglpEpKELCYcelzrL7q+cwF78Kqx+x5kE5dDEIK3qv5Z9uyvPIR9aVkLRnsp9ohKdc+y9fghJnSGpq7MeEf+dH90iOowxvdIY0yuNigpLQVEdzc3ucQpqEZHGpEUHOPM3MPyBKhOD/NFZOp7hXIBWFxODFOU7reO8KmG8czXs21m5T3icE8KnXOQ8JnV2WsnRiSf2s3EmGomLbBq3cymoRUQao0AdTQxSss/poj4Yxnmrned7t1TuExLlhHCns5wgTuriBHNMcoO5hczLFNQiIo1dfGsYfh8Mu7dyYpBP/gbz/uJcKd7nWvxlEbB92RHnkFfBt5s4NA+3P8yZEa3t6ZXnkJO6QFyrBne1eUOioBYRaSqONTHItOsZAjD34H4BaJEBqb2dK8gPhnLzdu5fmNYEKahFRJqiIyYG2ZAzhXZ9Rzpd1s07OF3n4gkKahGRpiw4Mcimr6Fdtyy3q5Fq6KSCiIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHjYcYPaGPO8MWanMWb5MV43xpi/GmPWGWOWGmN6132ZIiIiTVNNWtQvAud8x+vnAhnBZTww6cTLEhEREahBUFtrc4BvvmOXMcBk65gPxBtjUuqqQBERkaasLs5RpwGbq6znBreJiIjICQrUwWeYarbZanc0ZjxO9ziJiYlkZ2fXwY+XYyksLNQxPgl0nOufjnH90zH2rroI6lygVZX1dGBrdTtaa58BngHIzMy0WVlZdfDj5Viys7PRMa5/Os71T8e4/ukYe1dddH3PAK4JXv09AMi31m6rg88VERFp8o7bojbGvAZkAQnGmFzgV0AIgLX2KWAmMApYB+wHrquvYkVERJqa4wa1tfaK47xugZ/UWUUiIiJyiEYmExER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4mIJaRETEwxTUIiIiHqagFhER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4mIJaRETEwxTUIiIiHqagFhER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4mIJaRETEwxTUIiIiHqagFhER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4mIJaRETEwxTUIiIiHqagFhER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4mIJaRETEwxTUIiIiHqagFhER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4WI2C2hhzjjFmjTFmnTHm59W8nmWMyTfGLA4uD9Z9qSIiIk1P4Hg7GGP8wJPAmUAusMAYM8Nau/KIXedYa0fXQ40iIiJNVk1a1KcB66y16621JcDrwJj6LUtERESgBi1qIA3YXGU9F+hfzX4DjTFLgK3A3dbaFUfuYIwZD4wHSExMJDs7u9YFS80VFhbqGJ8EOs71T8e4/ukYe1dNgtpUs80esb4IaGOtLTTGjALeAjKOepO1zwDPAGRmZtqsrKzaVSu1kp2djY5x/dNxrn86xvVPx9i7atL1nQu0qrKejtNqPsRau9daWxh8PhMIMcYk1FmVIiIiTVRNgnoBkGGMaWeMCQXGAjOq7mCMSTbGmODz04Kfu7uuixUREWlqjtv1ba0tM8b8FPgA8APPW2tXGGMmBF9/CrgEuMkYUwYcAMZaa4/sHhcREZFaqsk56oPd2TOP2PZUled/B/5et6WJiIiIRiYTERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ9TUIuIiHiYglpERMTDFNQiIiIepqAWERHxMAW1iIiIhymoRUREPExBLSIi4mEKahEREQ+rUVAbY84xxqwxxqwzxvy8mteNMeavwdeXGmN6132pIiIiTc9xg9oY4weeBM4FugJXGGO6HrHbuUBGcBkPTKrjOkVERJqkmrSoTwPWWWvXW2tLgNeBMUfsMwaYbB3zgXhjTEod1yoiItLk1CSo04DNVdZzg9tqu4+IiIjUUqAG+5hqttnvsQ/GmPE4XeMAxcaY5TX4+fL9JQC73C6iCdBxrn86xvVPx/jkyKztG2oS1LlAqyrr6cDW77EP1tpngGcAjDGfW2v71qpaqRUd45NDx7n+6RjXPx3jk8MY83lt31OTru8FQIYxpp0xJhQYC8w4Yp8ZwDXBq78HAPnW2m21LUZEREQOd9wWtbW2zBjzU+ADwA88b61dYYyZEHz9KWAmMApYB+wHrqu/kkVERJqOmnR9Y62diRPGVbc9VeW5BX5Sy5/9TC33l9rTMT45dJzrn45x/dMxPjlqfZyNk7EiIiLiRRpCVERExMNcCerjDUkqJ8YY08oY87ExZpUxZoUx5ja3a2qsjDF+Y8wXxph33K6lsTLGxBtjphpjVgf/Tg90u6bGxhhzR/D/iuXGmNeMMeFu19TQGWOeN8bsrHobsjGmuTHmv8aYL4OPzWryWSc9qGs4JKmcmDLgLmttF2AA8BMd43pzG7DK7SIauSeA9621nYGe6HjXKWNMGnAr0Nda2w3nouGx7lbVKLwInHPEtp8Ds6y1GcCs4PpxudGirsmQpHICrLXbrLWLgs8LcP5j00hxdcwYkw6cBzzrdi2NlTEmFhgKPAdgrS2x1u5xt6pGKQBEGGMCQCTVjIMhtWOtzQG+OWLzGOCl4POXgAtr8lluBLWGGz2JjDFtgVOBz9ytpFH6C/AzoMLtQhqx9kAe8ELwFMOzxpgot4tqTKy1W4A/AV8D23DGwfiPu1U1Wi0PjjESfEyqyZvcCOoaDTcqJ84YEw1MA2631u51u57GxBgzGthprV3odi2NXADoDUyy1p4K7KOG3YVSM8HzpGOAdkAqEGWMucrdqqQqN4K6RsONyokxxoTghPQr1trpbtfTCA0GLjDGbMQ5fTPCGPNPd0tqlHKBXGvtwR6hqTjBLXXnDGCDtTbPWlsKTAcGuVxTY7Xj4MySwcedNXmTG0FdkyFJ5QQYYwzOOb1V1trH3K6nMbLW/sJam26tbYvzd/gja61aIXXMWrsd2GyMOTiRwUhgpYslNUZfAwOMMZHB/ztGogv26ssMYFzw+Tjg3zV5U41GJqtLxxqS9GTX0cgNBq4GlhljFge33RccYU6kobkFeCX4xX49GqK4TllrPzPGTAUW4dwx8gUapeyEGWNeA7KABGNMLvAr4A/AFGPM9ThfkC6t0WdpZDIRERHv0shkIiIiHqagFhER8TAFtYiIiIcpqEVERDxMQS0iIuJhCmoREREPU1CLiIh4mIJaRETEw/4fScRncvJbaUIAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 576x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_learning_curves(history, label, epochs, min_value, max_value):\n",
    "    data = {}\n",
    "    data[label] = history.history[label]\n",
    "    data['val_'+label] = history.history['val_'+label]\n",
    "    pd.DataFrame(data).plot(figsize=(8, 5))\n",
    "    plt.grid(True)\n",
    "    plt.axis([0, epochs, min_value, max_value])\n",
    "    plt.show()\n",
    "    \n",
    "plot_learning_curves(history, 'accuracy', epochs, 0, 1)\n",
    "plot_learning_curves(history, 'loss', epochs, 0, 2.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 迁移学习--RestNet50 finetune\n",
    "### 数据增强"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 1098 images belonging to 10 classes.\n",
      "Found 272 images belonging to 10 classes.\n",
      "1098 272\n"
     ]
    }
   ],
   "source": [
    "# 将图片转化成同一尺寸\n",
    "height = 224\n",
    "width = 224\n",
    "channels = 3\n",
    "batch_size = 8\n",
    "num_classes = 10\n",
    "\n",
    "# 读取图片并增强\n",
    "train_datagen = keras.preprocessing.image.ImageDataGenerator(\n",
    "    preprocessing_function=keras.applications.resnet50.preprocess_input,\n",
    "    rotation_range= 40, # 旋转范围\n",
    "    width_shift_range= 0.2, # 水平位移\n",
    "    height_shift_range= 0.2, # 竖直平移\n",
    "    shear_range= 0.2, # 剪切范围\n",
    "    zoom_range= 0.2, # 缩放范围\n",
    "    horizontal_flip= True, # 随机水平翻转\n",
    "    fill_mode= 'nearest', # 对空白位置的填充规则\n",
    ")\n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "    train_dir, target_size = (height, width), batch_size = batch_size,\n",
    "    seed = 7, shuffle = True, class_mode = 'categorical')\n",
    "\n",
    "valid_datagen = keras.preprocessing.image.ImageDataGenerator(\n",
    "    preprocessing_function=keras.applications.resnet50.preprocess_input)\n",
    "valid_generator = valid_datagen.flow_from_directory(\n",
    "    valid_dir, target_size = (height, width), batch_size = batch_size,\n",
    "    seed = 7, shuffle = False, class_mode = 'categorical')\n",
    "\n",
    "train_num = train_generator.samples\n",
    "valid_num = valid_generator.samples\n",
    "print(train_num, valid_num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(8, 224, 224, 3) (8, 10)\n",
      "[[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n",
      " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]\n",
      " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]\n",
      " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n",
      " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n",
      " [0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]]\n"
     ]
    }
   ],
   "source": [
    "for i in range(1):\n",
    "    x, y = train_generator.next()\n",
    "    print(x.shape, y.shape)\n",
    "    print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### resnet50 finetune 模型构建"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "resnet50_fine_tune = keras.models.Sequential()\n",
    "resnet50_fine_tune.add(keras.applications.ResNet50(\n",
    "    include_top = False, pooling = 'avg', weights = 'imagenet'))\n",
    "resnet50_fine_tune.add(keras.layers.Dense(num_classes, activation='softmax'))\n",
    "resnet50_fine_tune.layers[0].trainable = False\n",
    "\n",
    "resnet50_fine_tune.compile(\n",
    "    loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])\n",
    "resnet50_fine_tune.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n"
     ]
    }
   ],
   "source": [
    "epochs = 10\n",
    "history_resnet = resnet50_fine_tune.fit_generator(\n",
    "    train_generator,\n",
    "    steps_per_epoch= train_num // batch_size,\n",
    "    epochs=epochs,\n",
    "    validation_data=valid_generator,\n",
    "    validation_steps= valid_num // batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_learning_curves(history_resnet, 'accuracy', epochs, 0, 1)\n",
    "plot_learning_curves(history_resnet, 'loss', epochs, 0, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 另一种resnet50 finetune，对resnet最后几层进行训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "resnet50 = keras.applications.ResNet50(\n",
    "    include_top = False, pooling = 'avg', weights = 'imagenet')\n",
    "resnet50.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for layer in resnet50.layers[0:-5]:\n",
    "    layer.trainable = False\n",
    "\n",
    "resnet50_new = keras.models.Sequential([\n",
    "    resnet50,\n",
    "    keras.layers.Dense(num_classes, activation='softmax')\n",
    "])\n",
    "\n",
    "resnet50_new.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])\n",
    "resnet50_new.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "epochs = 10\n",
    "history_resnet_new = resnet50_new.fit_generator(\n",
    "    train_generator,\n",
    "    steps_per_epoch= train_num // batch_size,\n",
    "    epochs=epochs,\n",
    "    validation_data=valid_generator,\n",
    "    validation_steps= valid_num // batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_learning_curves(history_resnet_new, 'accuracy', epochs, 0, 1)\n",
    "plot_learning_curves(history_resnet_new, 'loss', epochs, 0, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
